﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Sgr.AuditLogs;
using Sgr.Generator;
using Sgr.Identity;
using Sgr.Identity.Services;
using Sgr.Utilities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Sgr.Indentity.Application.Commands
{
    public abstract class IdentityCommandHandleBase
    {
        protected readonly IHttpContextAccessor _httpContextAccessor;
        protected readonly IAccountAuthService _accountService;

        public IdentityCommandHandleBase(IAccountAuthService accountService, IHttpContextAccessor httpContextAccessor)
        {
            _accountService = accountService;
            _httpContextAccessor = httpContextAccessor;
        }

        /// <summary>
        /// 创建登录日志
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="loginName">登录用户名称</param>
        /// <param name="displayName">用户显示名称</param>
        /// <param name="loginWay">登录方式</param>
        /// <param name="status">登录状态</param>
        /// <param name="remarks">备注</param>
        /// <param name="orgId">所属组织</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task CreateLoginLogAsync(
            string userId,
            string loginName,
            string displayName,
            AccountLoginWay loginWay,
            bool status,
            string remarks,
            string orgId,
            CancellationToken cancellationToken)
        {
            var httpUserAgentProvider = _httpContextAccessor.HttpContext?.RequestServices?.GetService<IHttpUserAgentProvider>();
            if (httpUserAgentProvider != null && _httpContextAccessor.HttpContext != null)
            {
                var httpUserAgent = httpUserAgentProvider.Analysis(_httpContextAccessor.HttpContext);

                await _accountService.CreateLoginLogAsync(
                    userId,
                    loginName,
                    displayName,
                    _httpContextAccessor.HttpContext!.GetClientIpAddress(),
                    loginWay,
                    httpUserAgent?.BrowserInfo ?? "",
                    httpUserAgent?.Os ?? "",
                    status,
                    remarks,
                    orgId,
                    cancellationToken);
            }
        }

        protected Claim[] GetClaims(string jti, Account account)
        {
            return new Claim[]
            {
                new Claim(JwtRegisteredClaimNames.Jti,jti),//JwtRegisteredClaimNames.Jti JWT的唯一标识符，可用于后续JWT黑名单处理逻辑
                new Claim(ClaimTypes.NameIdentifier, account.LoginName),
                new Claim(ClaimTypes.Name, account.DisplayName),
                //new Claim(ClaimTypes.Role,string.Join("," ,account.Roles)),   //出于安全考虑，令牌中不再包含角色列表
                new Claim(Constant.CLAIM_USER_ID, account.Id.ToString()),
                new Claim(Constant.CLAIM_USER_ORGID, account.OrgId.ToString())
            };
        }

        protected async Task AddTokenToBlacklistAsync(
            ITokenBlacklistService tokenBlacklistService,
            JwtSecurityToken jwtToken,
            JwtOptions jwtOptions)
        {
            var jti = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Jti)?.Value;
            // 将令牌添加至黑名单
            if (!string.IsNullOrEmpty(jti))
            {
                // 计算令牌剩余有效期
                var expClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Exp)?.Value;
                TimeSpan remainingTime = TimeSpan.FromSeconds(jwtOptions.ExpireSeconds);

                if (!string.IsNullOrEmpty(expClaim) && long.TryParse(expClaim, out long expTimestamp))
                {
                    var expiryTime = DateTimeHelper.TimestampSecondsToDateTimeOffset(expTimestamp);
                    remainingTime = expiryTime - DateTimeOffset.UtcNow;
                    if (remainingTime.TotalSeconds <= 0)
                        remainingTime = TimeSpan.FromSeconds(1); // 最少保持1秒
                }

                await tokenBlacklistService.AddToBlacklistAsync(jti, remainingTime);
            }
        }

        /// <summary>
        /// 刷新令牌写入Cookie
        /// </summary>
        /// <param name="httpResponse"></param>
        /// <param name="refreshToken"></param>
        /// <param name="jwtOptions"></param>
        protected void WriteRefreshTokenToCookie(HttpResponse? httpResponse, string refreshToken, JwtOptions jwtOptions)
        {
            if (httpResponse == null)
                return;

            var cookieOptions = new CookieOptions
            {
                HttpOnly = true,
                Secure = jwtOptions.RefreshTokenSecure,                                                 // 生产环境中应启用 HTTPS
                SameSite = HttpCookieOptions.ToSameSiteMode(jwtOptions.RefreshTokenSameSite),           // 根据需求设置
                Expires = DateTimeOffset.UtcNow.AddMinutes(jwtOptions.RefreshTokenExpireMinutes)        // 设置适当的过期时间
            };

            if (!string.IsNullOrEmpty(jwtOptions.RefreshTokenDomain))
            {
                cookieOptions.Domain = jwtOptions.RefreshTokenDomain;
            }

            if (!string.IsNullOrEmpty(jwtOptions.RefreshTokenPath))
            {
                cookieOptions.Path = jwtOptions.RefreshTokenPath;
            }

            httpResponse!.Cookies.Append(jwtOptions.RefreshTokenCookieName ?? "refreshToken", refreshToken, cookieOptions);
        }

        /// <summary>
        /// 创建令牌
        /// </summary>
        /// <param name="claims"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        protected string CreateAccessToken(IEnumerable<Claim> claims, JwtOptions options)
        {
            var expiry = DateTime.UtcNow.AddSeconds(options.ExpireSeconds);

            var securityKey = new SymmetricSecurityKey(options.KeyBytes);
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

            var tokenDescriptor = new JwtSecurityToken(
                issuer: options.Issuer,
                audience: options.Audience,
                claims: claims,
                //notBefore: utcNow.DateTime,
                expires: expiry,
                signingCredentials: credentials);

            return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
        }

        /// <summary>
        /// 解析访问令牌，提取用户基本信息
        /// </summary>
        /// <param name="token">访问令牌</param>
        /// <returns>包含用户ID、用户名称和组织ID的元组，如果解析失败则返回null</returns>
        protected JwtSecurityToken? GetJwtTokenFromAccessToken(string token)
        {
            if (string.IsNullOrWhiteSpace(token))
                return null;

            // 移除可能的授权方案前缀
            token = token.Replace($"{JwtBearerDefaults.AuthenticationScheme} ", "");

            try
            {
                var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
                if (jwtSecurityTokenHandler.CanReadToken(token))
                {
                    return jwtSecurityTokenHandler.ReadJwtToken(token);

                    //// 提取所需的声明值
                    //var userId = jwtToken.Claims.FirstOrDefault(c => c.Type == Constant.CLAIM_USER_ID)?.Value;
                    //var userName = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
                    //var orgId = jwtToken.Claims.FirstOrDefault(c => c.Type == Constant.CLAIM_USER_ORGID)?.Value;
                    //var jti = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Jti)?.Value;

                    //// 至少需要用户ID才返回结果
                    //if (!string.IsNullOrEmpty(userId))
                    //{
                    //    return (userId, userName, orgId, jti);
                    //}
                }
            }
            catch (Exception ex)
            {
                // 记录异常但不向上抛出，让调用者处理null返回值
                System.Diagnostics.Debug.WriteLine($"解析访问令牌出错: {ex.Message}");
            }

            return null;
        }

        /// <summary>
        /// 验证令牌
        /// </summary>
        /// <param name="token"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        protected ClaimsPrincipal? ValidateAccessToken(string token, JwtOptions options)
        {
            if (string.IsNullOrWhiteSpace(token))
                return default;

            token = token.Replace($"{JwtBearerDefaults.AuthenticationScheme} ", "");
            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,                                                  //是否验证发行商
                ValidateAudience = true,                                                //是否验证受众者
                ValidateLifetime = true,                                                //是否验证失效时间
                ValidateIssuerSigningKey = true,                                        //是否验证签名键
                ValidIssuer = options.Issuer,
                ValidAudience = options.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.Key)),
                ClockSkew = TimeSpan.FromSeconds(options.ClockSkewSeconds),             //过期时间容错值，解决服务器端时间不同步问题（秒）
                RequireExpirationTime = true,
            };

            var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();

            ClaimsPrincipal? claimsPrincipal;

            try
            {
                claimsPrincipal = jwtSecurityTokenHandler.ValidateToken(token, validationParameters, out _);
            }
            catch (SecurityTokenExpiredException)
            {
                //表示过期
                return default;
            }
            catch (SecurityTokenException)
            {
                //表示token错误
                return default;
            }
            catch
            {
                return default;
            }
            return claimsPrincipal;
        }
    }
}